﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Kohonen
{
    public class Neuronio
    {
        #region Atributos
        /// <summary>
        /// Quantidade de Entradas do Neurônio
        /// </summary>
        private int qtdEntradas;

        /// <summary>
        /// Vetor de Pesos no Neurônio
        /// </summary>
        public double[] W { get; set; }

        /// <summary>
        /// Taxa de Aprendizagem do Neurônio
        /// </summary>
        private double taxaAprendizado;

        /// <summary>
        /// Quantidade de Vitórias do Neurônio
        /// </summary>
        public int QtdVitorias{get;set;}
        #endregion

        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="taxa">Taxa de Aprendizagem Aplicada</param>
        /// <param name="qtdEntradas">Quantidade de Entradas</param>
        /// <param name="inicializaRandom">Inicializa os pesos randomicamente</param>
        public Neuronio(double taxa, int qtdEntradas, bool inicializaRandom)
        {
            this.qtdEntradas = qtdEntradas;
            this.taxaAprendizado = taxa;
            InicializarPesos(inicializaRandom);
        }

        #region Métodos

        /// <summary>
        /// Método para realizar a inicialização dos pesos
        /// </summary>
        /// <param name="inicializaRandom">Verifica se a inicialização é randomica</param>
        private void InicializarPesos(bool inicializaRandom)
        {
           //Inicializar pesos, caso seja randomica utilizar a classe Util
           // Caso não seja Randomica, utilizar a regra que desejar para a inicializaçãoqtfrn

            W = new double[qtdEntradas];

            if (inicializaRandom)
            {
                for (int i = 0; i < qtdEntradas; i++)
                {
                    W[i] = Util.Random(400);
                }
            }
            else
            {
                int raio = 100;
                int angulo = Util.Random(360);
                W[0] = raio * Math.Cos(angulo)+200;
                W[1] = raio * Math.Sin(angulo)+200;
                
            }
        }


        /// <summary>
        /// Método que retorna a distância euclidiana entre o Dado apresentado e o Peso
        /// </summary>
        /// <param name="dado">Dado para o Calculo</param>
        /// <returns></returns>
        public double Distancia(double[] dado)
        {
            // Encontra a distancia euclidiana entre o Dado X e o Peso W
           double resultado = 0;

            for (int i = 0; i < dado.Length; i++)
			{
                resultado += Math.Pow(dado[i] - W[i], 2);
			}

            //distancia +=  Math.Pow(((Math.Pow((X[0] - W[0]),2)+Math.Pow((X[1] - W[1]),2)),0.5);

            return Math.Sqrt(resultado);
        }

        /// <summary>
        /// Método para realizar a Atualização do Peso, com respeito ao grau de vizinhança
        /// </summary>
        /// <param name="dado">Dado apresentado para o calculo</param>
        /// <param name="grau">Grau de vizinhança</param>
        public void Atualizar(double[] dado, int grau)
        {
            //Aualiza os pesos respeitando o grau da vizinhança
            for (int i = 0; i < dado.Length; i++)
            {
                W[i] += (taxaAprendizado/grau) * (dado[i] - W[i]); 
            }           

        }
        #endregion


    }
}
